package com.umgsai.wx.backend.test;

import java.util.Map;
import java.util.HashMap;

public class LRUCache<K, V> {

    private Map<K, Node<K, V>> cacheMap = null;
    private final int capacity;
    private final Node<K, V> head;
    private final Node<K, V> tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        cacheMap = new HashMap<>();
        head = new Node<>();
        tail = new Node<>();
        head.next = tail;
        tail.prev = head;
    }

    public V get(K key) {
        // 从Map中取值
        Node<K, V> node = cacheMap.get(key);
        if (node == null) {
            // 如果没有，则返回null
            return null;
        }
        // 如果缓存中有，移动到链表头部，最近有使用
        moveToHead(node);
        return node.value;
    }

    public void put(K key, V value) {
        // 如果缓存中有，则更新值，并移动到链表头部
        Node<K, V> node = cacheMap.get(key);
        if (node != null) {
            node.value = value;
            moveToHead(node);
            return;
        }
        // 缓存中没有，则创建一个节点，并添加到链表头部
        node = new Node<>();
        node.value = value;
        cacheMap.put(key, node);
        moveToHead(node);
        // 缓存已满，则删除链表尾部节点，尾部为最少使用的节点
        if (cacheMap.size() > capacity) {
            removeTail();
        }
    }

    public void moveToHead(Node<K, V> node) {
        // 将节点从原位置删除
        remove(node);
        // 添加到链表头部
        Node<K, V> temp = head.next;
        head.next = node;
        node.prev = head;
        node.next = temp;
        temp.prev = node;
    }

    public void remove(Node<K, V> node) {
        if (node.prev == null || node.next == null) {
            // 链表头，或链表尾，直接返回
            return;
        }
        Node<K, V> temp = node.prev;
        temp.next = node.next;
        node.next.prev = temp;
    }

    public void removeTail() {
        // 移除尾节点
        Node<K, V> temp = tail.prev;
        remove(temp);
    }

    public static void main(String... args) {
        LRUCache<Integer, Integer> lruCache = new LRUCache<>(3);
        lruCache.put(1, 1);
        lruCache.put(2, 2);
        lruCache.put(3, 3);
        lruCache.printCache();
        lruCache.get(2);
        lruCache.printCache();
        lruCache.put(4, 4);
        lruCache.printCache();
    }

    public void printCache() {
        Node<K, V> temp = head;
        while (temp != null) {
            if (temp == head || temp == tail) {
                temp = temp.next;
                continue;
            }
            System.out.print(temp.value + "\t");
            temp = temp.next;
        }
        System.out.println("\n-------------------");
    }

    public static class Node<K, V> {
        K key;
        V value;
        Node<K, V> prev;
        Node<K, V> next;
    }
}